package io.task.cc.network.maintain;

import io.task.cc.network.ChannelContext;
import io.task.cc.network.Config;
import io.task.cc.network.utils.lock.LockUtils;
import io.task.cc.network.utils.lock.LockedMap;
import io.task.cc.network.utils.lock.LockedSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

/**
 * 多对多  (group <--> ChannelContext)<br>
 */
public class Groups {

    /**
     * 对ChannelContext进行排序的比较器
     * 该对象必须在服务启动前进行设置，并且不要再去修改，否则会导致有的排序了，有的没有排序
     */
    private Comparator<ChannelContext> channelContextComparator = null;
    /**
     * The log.
     */
    private static final Logger log = LoggerFactory.getLogger(Groups.class);
    /**
     * 一个组有哪些客户端<br>
     * key: groupid<br>
     * value: SetWithLock<ChannelContext><br>
     */
    private final LockedMap<String, LockedSet<ChannelContext>> map = new LockedMap<>(new HashMap<String, LockedSet<ChannelContext>>());
    private final String rwKey = "_tio_groups_bind__";

    /**
     * 和组绑定
     *
     * @param groupid
     * @param channelContext
     */
    public void bind(String groupid, ChannelContext channelContext) {
        bind(groupid, channelContext, true);
    }

    /**
     * 和组绑定
     *
     * @param groupid
     * @param channelContext
     * @param callbackListener 是否回调GroupListener
     */
    public void bind(String groupid, ChannelContext channelContext, boolean callbackListener) {


        LockedSet<ChannelContext> channelSet = map.get(groupid);
        if (channelSet == null) {
            try {
                LockUtils.runWriteOrWaitRead(rwKey + groupid, this, () -> {
//					@Override
//					public void read() {
//					}
//
//					@Override
//					public void write() {
//						SetWithLock<ChannelContext> channelSet1 = groupmap.get(groupid);
                    if (map.get(groupid) == null) {
//							channelSet1 = new SetWithLock<>(MaintainUtils.createSet(channelContextComparator));
                        //groupmap.put(groupid, new SetWithLock<>(MaintainUtils.createSet(channelContextComparator)));
                    }
//					}
                });
            } catch (Exception e) {
                log.error(e.toString(), e);
            }
            channelSet = map.get(groupid);
        }
        channelSet.add(channelContext);
        channelContext.getGroups().add(groupid);

        if (callbackListener) {

        }
    }

    /**
     * 一个组有哪些客户端
     *
     * @param groupid
     * @return
     * @author tanyaowu
     */
    public LockedSet<ChannelContext> clients(Config config, String groupid) {


        return map.get(groupid);
    }

    /**
     * @return the groupmap
     */
    public LockedMap<String, LockedSet<ChannelContext>> getMap() {
        return map;
    }

    /**
     * 某个客户端在哪些组中
     *
     * @param channelContext
     * @return
     * @author tanyaowu
     */
    public LockedSet<String> groups(ChannelContext channelContext) {
        Config config = channelContext.config;


        return channelContext.getGroups();
    }

    /**
     * 与所有组解除绑定
     *
     * @param channelContext
     * @author tanyaowu
     */
    public void unbind(ChannelContext channelContext) {
        unbind(channelContext, true);
    }

    /**
     * @param channelContext
     * @param callbackListener 是否回调GroupListener
     */
    public void unbind(ChannelContext channelContext, boolean callbackListener) {


        try {
            LockedSet<String> lockedSet = channelContext.getGroups();
            WriteLock writeLock = lockedSet.writeLock();
            writeLock.lock();
            try {
                Set<String> groups = lockedSet.getT();
                if (groups != null && groups.size() > 0) {
                    for (String groupid : groups) {
                        try {
                            unbind(groupid, channelContext, false, callbackListener);
                        } catch (Exception e) {
                            log.error(e.toString(), e);
                        }
                    }
                    groups.clear();
                    channelContext.getGroups().clear();
                }
            } catch (Exception e) {
                log.error(e.toString(), e);
            } finally {
                writeLock.unlock();
            }

        } catch (Throwable e) {
            log.error(e.toString(), e);
        }
    }

    /**
     * 与指定组解除绑定
     *
     * @param groupid
     * @param channelContext
     */
    public void unbind(String groupid, ChannelContext channelContext) {
        unbind(groupid, channelContext, true);
    }

    /**
     * 与指定组解除绑定
     *
     * @param groupid
     * @param channelContext
     * @param deleteFromChannelContext
     */
    public void unbind(String groupid, ChannelContext channelContext, boolean deleteFromChannelContext) {
        unbind(groupid, channelContext, deleteFromChannelContext, true);
    }

    /**
     * @param groupid
     * @param channelContext
     * @param deleteFromChannelContext
     * @param callbackListener         是否回调GroupListener
     */
    public void unbind(String groupid, ChannelContext channelContext, boolean deleteFromChannelContext, boolean callbackListener) {


        try {
            LockedSet<ChannelContext> channelSet = map.get(groupid);
            if (channelSet != null) {
                boolean ss = channelSet.remove(channelContext);
                if (!ss) {
                    log.warn("{}, 移除失败,group:{} cid:{}", channelContext, groupid, channelContext.getId());
                }

                if (deleteFromChannelContext) {
                    channelContext.getGroups().remove(groupid);
                }

                if (callbackListener) {

                }

                //如果该群组没有任何连接，就把这个群组从map中删除，以释放空间
                if (channelSet.getT().size() == 0) {
                    map.remove(groupid);
                }
            }
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
    }

    public Comparator<ChannelContext> getChannelContextComparator() {
        return channelContextComparator;
    }

    public void setChannelContextComparator(Comparator<ChannelContext> channelContextComparator) {
        this.channelContextComparator = channelContextComparator;
    }
}
